Существует чудесный PEP3102, который, в общем-то, никаких новых механик в язык не привносит, но позволяет добавить порядку в код. Его название — keyword-only arguments — может слегка сбить с толку, но сейчас всё станет понятно.
Взгляните на сигнатуру этой функции. Неважно, что именно она делает, главное, что у неё есть порядковые аргументы и именованные:
def upload_to_s3(bucket, file, encrypt=True):
Вроде всё хорошо. Но есть один момент. Оба этих варианта вызова функции равнозначны и дозволены:
upload_to_s3(bucket, file, True)
upload_to_s3(bucket, file, encrypt=True)
Хорошо ли это? Пока вы помните, какой именно именованный аргумент скрывается за безличным True
— да. В маленьких
скриптах, в небольших программках это совершенно легитимно. Если вам не лень смотреть каждый раз на сигнатуру функции,
пользуясь подсказкой IDE, или переходить к её определению — тоже всё ок. Но ведь лень же, да? Лень.
Через некоторое время функция может получить ещё один именованный параметр. Например, так:
def upload_to_s3(bucket, file, duplicate_to_glacier=False, encrypt=True):
И всё. Проблема даже не в том, что нужно ходить к сигнатуре, чтобы вспомнить, какой аргумент за что отвечает. Проблема
гораздо серьёзней: если вы или кто-то другой добавляет ещё один именованный аргумент до тех, что уже существуют,
логика сломается. Везде, где функция вызывается без именованных аргументов. Теперь вызов upload_to_s3(bucket, file, True)
будет означать не «зашифровать файл в S3», а «дублировать файл в Glacier».
Чтобы полностью убрать возможность таких ошибок, нужно добавить звёздочку перед именованными аргументами в сигнатуре:
def upload_to_s3(bucket, file, *, encrypt=True):
Теперь попытка вызвать функцию неправильно бросит исключение TypeError
:
TypeError: upload_to_s3() takes 2 positional arguments but 3 were given
Перечислю убитых зайцев: